package com.example.jwt.config;

import com.example.jwt.entity.security.JwtAuthenticationToken;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.*;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;

@Slf4j
public class JwtAuthenticationProvider implements AuthenticationProvider {

    private final UserDetailsService userDetailsService;
    private final PasswordEncoder passwordEncoder;

    public JwtAuthenticationProvider(UserDetailsService userDetailsService, PasswordEncoder passwordEncoder) {
        this.userDetailsService = userDetailsService;
        this.passwordEncoder = passwordEncoder;
    }

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        UserDetails userDetails = userDetailsService.loadUserByUsername(authentication.getName());
        // 进行账户状态的基本校验
        defaultCheck(userDetails);
        // 当执行此方法, 参数 authentication 已经通过 supports() 方法的校验, 所以可以直接强转
        JwtAuthenticationToken jwtLoginToken = (JwtAuthenticationToken) authentication;
        // 进行登陆的用户名密码校验
        additionalAuthenticationChecks(userDetails, jwtLoginToken);
        // 检验成功之后重新构建一个 Authentication
        return createSuccessAuthentication(jwtLoginToken, userDetails);
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return (JwtAuthenticationToken.class.isAssignableFrom(authentication));
    }

    /**
     * 根据用户信息进行用户账号状态的基本校验
     *
     * @param user
     */
    private void defaultCheck(UserDetails user) {
        if (!user.isAccountNonExpired()) {
            throw new AccountExpiredException("账户已过期");
        } else if (!user.isAccountNonLocked()) {
            throw new LockedException("账户已被锁定");
        } else if (!user.isCredentialsNonExpired()) {
            throw new CredentialsExpiredException("密码已过期");
        } else if (!user.isEnabled()) {
            throw new DisabledException("账户不可用");
        }
    }

    /**
     * 登陆用户名密码 与 数据库用户名密码 进行比对
     *
     * @param userDetails    数据库用户信息
     * @param authentication 登陆提交的用户信息
     * @throws AuthenticationException 比对失败, 抛出校验失败的异常
     */
    private void additionalAuthenticationChecks(UserDetails userDetails,
                                                JwtAuthenticationToken authentication) throws AuthenticationException {
        if (authentication.getCredentials() == null) {
            log.debug("用户 [{}] 校验失败, 登陆认证未提供密码", authentication.getName());
            throw new BadCredentialsException("密码错误");
        }
        String presentedPassword = authentication.getCredentials().toString();
        if (!this.passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {
            log.debug("用户 [{}] 校验失败, 密码不匹配", authentication.getName());
            throw new BadCredentialsException("密码错误");
        }
    }

    private Authentication createSuccessAuthentication(Authentication authentication,
                                                       UserDetails user) {
        JwtAuthenticationToken result = new JwtAuthenticationToken(user, authentication.getCredentials(), user.getAuthorities());
        result.setDetails(authentication.getDetails());
        log.debug("成功校验用户 -> {}", result);
        return result;
    }
}
